// ======================================================================
// Copyright (C) 2013 Hell-Prototypes. / www.hellprototypes.com
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
// ======================================================================

`timescale 10ns/1ns

module SRAM(
		input  		CLK_Core,
		input  		nRST,
		inout [15:0]RAM1_DATA,
		inout [15:0]RAM2_DATA,
		output[17:0]RAM_ADDR,
		output      nRAM1_RD_EN,
		output      nRAM1_WR_EN,
		output      nRAM2_RD_EN,
		output      nRAM2_WR_EN,
		output		nRAM_CS,
		
//		input		RST_RAM_Wr_ADDR,

		output reg  FIFO_RD_EN,
		input       FIFO_DATA_EMPTY,
		input [15:0]FIFO_RD_DATA,
		
		output	reg	FIFO_WR_EN,
		output	reg	[15: 0] FIFO_WR_DATA,
		input   	FIFO_FULL
		
//		input		RAM_Read_EN,
//		input		RST_RAM_RD_ADDR
	);
parameter 
	IDLE	= 1'b0,
	S1		= 1'b1;

parameter
	INPUT  = 1'b0,
	OUTPUT = 1'b1;
//=============================================================================
reg  [17:0] RAM_Rd_ADDR, next_RAM_Rd_ADDR;
reg  [17:0] RAM_Wr_ADDR, next_RAM_Wr_ADDR;

reg  Data_inout, next_Data_inout;

reg  [15: 0] RAM_Data, next_RAM_Data;
reg  nRAM_RD_EN, next_nRAM_RD_EN;
reg  nRAM_WR_EN, next_nRAM_WR_EN;

reg  next_FIFO_RD_EN;
reg  next_FIFO_WR_EN;
reg  [15: 0] next_FIFO_WR_DATA;

wire [15: 0] RAM_RD_Data;

reg  state, next_state;
reg  wr_ram_ready, next_wr_ram_ready;

wire data_buffered = RAM_Rd_ADDR ^ RAM_Wr_ADDR;

//=============================================================================
assign RAM_RD_Data = RAM1_DATA[15:0]; //{RAM2_DATA[15:0], RAM1_DATA[15:0]};
assign RAM1_DATA[15:0] = (Data_inout==OUTPUT) ? RAM_Data[15: 0] : 16'hZZZZ;
assign RAM2_DATA[15:0] = (Data_inout==OUTPUT) ? RAM_Data[15: 0] : 16'hZZZZ;
assign RAM_ADDR = (Data_inout==OUTPUT) ? RAM_Wr_ADDR : RAM_Rd_ADDR;

assign nRAM1_RD_EN = nRAM_RD_EN;
assign nRAM2_RD_EN = 1; //nRAM_RD_EN;

assign nRAM1_WR_EN = nRAM_WR_EN;
assign nRAM2_WR_EN = 1; //nRAM_WR_EN;

assign nRAM_CS = 0;
//=============================================================================
always @(posedge CLK_Core or negedge nRST) begin
	if(!nRST) begin
		state		<= IDLE;
		RAM_Rd_ADDR <= 18'h00000;
		RAM_Wr_ADDR <= 18'h00000;
		Data_inout	<= INPUT;
		RAM_Data	<= 16'b0;
		nRAM_RD_EN	<= 1;
		nRAM_WR_EN	<= 1;
		FIFO_RD_EN	<= 0;
		FIFO_WR_EN	<= 0;
		FIFO_WR_DATA<= 16'b0;
		wr_ram_ready<= 0;
	end else begin
		state		<= next_state;
		RAM_Rd_ADDR	<= next_RAM_Rd_ADDR;
		RAM_Wr_ADDR	<= next_RAM_Wr_ADDR;
		Data_inout	<= next_Data_inout;
		RAM_Data	<= next_RAM_Data;
		nRAM_RD_EN	<= next_nRAM_RD_EN;
		nRAM_WR_EN	<= next_nRAM_WR_EN;
		FIFO_RD_EN	<= next_FIFO_RD_EN;
		FIFO_WR_EN	<= next_FIFO_WR_EN;
		FIFO_WR_DATA<= next_FIFO_WR_DATA;
		wr_ram_ready<= next_wr_ram_ready;
	end
end

always @* begin//RAM Data Control
	next_state			= state;
	next_RAM_Rd_ADDR	= RAM_Rd_ADDR;
	next_RAM_Wr_ADDR	= RAM_Wr_ADDR;
	next_Data_inout		= Data_inout;
	next_RAM_Data		= RAM_Data;
	next_nRAM_RD_EN		= 1;
	next_nRAM_WR_EN		= 1;
	next_FIFO_RD_EN		= 0;
	next_FIFO_WR_EN		= 0;
	next_FIFO_WR_DATA	= FIFO_WR_DATA;
	next_wr_ram_ready   = wr_ram_ready;

	if (!wr_ram_ready) begin
		if (!FIFO_DATA_EMPTY) begin
			if (!FIFO_RD_EN) begin
				next_FIFO_RD_EN = 1;
			end else begin
				next_wr_ram_ready = 1;
			end
		end
	end

	case(state)
		IDLE: begin
			if(wr_ram_ready) begin
				//Write to RAM
				next_Data_inout = OUTPUT;
				next_RAM_Data = FIFO_RD_DATA;
				next_nRAM_WR_EN = 0;
				next_RAM_Wr_ADDR = RAM_Wr_ADDR + 1;
				next_wr_ram_ready = 0;
			end else if ((!FIFO_FULL) && (!FIFO_WR_EN) && (nRAM_WR_EN) && data_buffered) begin
				//Read from RAM stage 1
				next_Data_inout = INPUT;
				next_nRAM_RD_EN = 0;
				next_RAM_Rd_ADDR = RAM_Rd_ADDR + 1;
				next_state = S1;
			end
		end
		S1: begin //Read from RAM stage 2
			next_FIFO_WR_DATA = RAM_RD_Data;
			next_FIFO_WR_EN = 1;
			next_state = IDLE;
		end
	endcase
end

endmodule
